metadata = read_tsv(here('data', 'metadata_merged.tsv'), col_types = cols()) %>%
rename(TimePoint = Timepoint) %>%
mutate(TimePoint = str_replace(TimePoint, '^', 'T'),
condition = str_c(CornVariety, FungalStrain, TissueExtraction, sep='_')) %>%
rename(Corn_Genotype = CornVariety, Fungal_Treatment = FungalStrain, Tissue_Extraction = TissueExtraction)
get_counts = function(.) otu_table(.) %>% as.data.frame() %>% rownames_to_column('feature_id') %>% as_tibble()
get_tax = function(.) tax_table(.) %>% data.frame() %>% rownames_to_column('feature_id') %>% as_tibble()
get_qza = function(filepath){read_qza(filepath)$data %>% as.data.frame() %>% rownames_to_column('SampleID')}
change_its_ids_to_match_16S = . %>%
mutate(SampleID = str_replace(SampleID, '(.*?)-(.*)', '\\2-\\1'),
SampleID = str_replace_all(SampleID, c('-B73'='-373', '-C322'='-322')))
change_16S_ids_to_match_ITS = . %>%
mutate(SampleID = str_replace(SampleID, '(.*?)-(.*)', '\\2-\\1'),
SampleID = str_replace_all(SampleID, c('-373'='-B73', '-322'='-C322')))
ps %>% imap(~get_counts(.x) %>%
left_join(reads_df[[.y]], by='feature_id') %>%
left_join(get_tax(.x), by='feature_id') %>%
rename(all_of(get_sample_data(.x) %>% pull(SampleID, name=condition_w_rep))) %>%
write_csv(here(str_glue('output/raw_counts_and_taxonomy_{.y}.csv')))
)
$`16S`
$ITS
NA
Removed taxa not assigned to a phylum. After removing these taxa, 1
16S samples was removed due to having total counts <= 1500.
ps_phylum_filt = list('16S' = subset_taxa(ps$`16S`, !is.na(Phylum) &
!Phylum %in% c('', 'uncharacterized', 'unidentified') &
!Kingdom %in% c('d__Eukaryota')) %>%
prune_samples(sample_sums(.) >= 1500, .) %>% prune_taxa(taxa_sums(.) > 0, .),
'ITS' = subset_taxa(ps$ITS, !is.na(Phylum) &
ps_phylum_filt_tax = ps_phylum_filt %>% map(get_tax)
ps_phylum_filt_counts = ps_phylum_filt %>% map(get_counts)
ps_phylum_filt_ra = map(ps_phylum_filt, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_phylum_filt
phyloseq-class experiment-level object
otu_table() OTU Table: [ 946 taxa and 48 samples ]
sample_data() Sample Data: [ 48 samples by 10 sample variables ]
tax_table() Taxonomy Table: [ 946 taxa by 7 taxonomic ranks ]
phy_tree() Phylogenetic Tree: [ 946 tips and 919 internal nodes ]
$ITS
phyloseq-class experiment-level objectsample_data() Sample Data: [ 50 samples by 9 sample variables ]
reads = list('16S' = read_qza(here('data', '16S', 'rep-seqs-filtered.qza'))$data,
'ITS' = read_qza(here('data', 'ITS', 'rep-seqs-filtered.qza'))$data)
ps_phylum_filt %>% imap(function(ps_phylum_filt, seq_type){
ps_phylum_filt %>% get_tax() %>%
summarize(across(-feature_id, ~sum(!is.na(.x))/ n())) %>%
pivot_longer(everything(), names_to = 'Taxonomic rank', values_to = seq_type)
reduce(full_join, by='Taxonomic rank') %>%
kable_classic(full_width = F, html_font = "Times New Roman")
Fraction of ASVs classified at each rank
| Taxonomic rank |
16S |
ITS |
| Kingdom |
1.00 |
1.00 |
| Phylum |
1.00 |
1.00 |
| Class |
1.00 |
0.99 |
| Order |
1.00 |
0.93 |
| Family |
0.99 |
0.89 |
| Genus |
0.91 |
0.86 |
| Species |
0.28 |
0.73 |
ps_phylum_filt %>% imap(function(ps_phylum_filt, seq_type){
ps_phylum_filt %>% sample_sums() %>% enframe(name = 'SampleID', value = 'total_counts') %>%
left_join(metadata, by='SampleID') %>%
mutate(seq_type = seq_type)
ggplot(aes(x=condition, y=total_counts)) +
geom_boxplot(outlier.shape = NA) +
ggbeeswarm::geom_quasirandom(alpha = 0.3, width=0.2, groupOnX=TRUE) +
facet_wrap(~seq_type, ncol=1, scales = 'free') +
scale_y_continuous(labels = scales::comma) +
labs(title='Total number of counts for each sample') +
ggeasy::easy_center_title()

Rarefying to 1569 counts.
ps_phylum_filt_rarefied = ps_phylum_filt %>% map(
~rarefy_even_depth(.x, sample.size = 1569, rngseed=1100 , replace=FALSE) %>%
prune_taxa(taxa_sums(.) > 0, .))
ps_phylum_filt_rarefied
phyloseq-class experiment-level object
$ITS
phyloseq-class experiment-level object
otu_table() OTU Table: [ 111 taxa and 50 samples ]
sample_data() Sample Data: [ 50 samples by 9 sample variables ]
tax_table() Taxonomy Table: [ 111 taxa by 7 taxonomic ranks ]phy_tree() Phylogenetic Tree: [ 111 tips and 109 internal nodes ]
prev = map(ps_phylum_filt_ra,
function(ps_phylum_filt_ra){
relative_counts = ps_phylum_filt_ra %>% get_counts()
tax = ps_phylum_filt_ra %>% get_tax()
prevalence = rowSums(.> 0) -1, #subtracted 1 because feature_id column is always counted
Phylum = tax$Phylum,
Genus = tax$Genus,
Species = tax$Species)})
prev_plots = prev %>% imap(function(prev, seq_type){
prev %>%
mutate(prevalence = prevalence / nsamples(ps_phylum_filt[[seq_type]])) %>%
geom_hline(yintercept = 0.05, alpha = 0.5, linetype = 2) +
geom_point(size = 2, alpha = 0.6) +
scale_x_log10() + xlab("Total Relative Abundance") + ylab("Prevalence [Fraction Samples]") +
facet_wrap(~Phylum) +
labs(title = glue('{seq_type} ASV Prevalence')) +
theme(plot.title = element_text(hjust = 0.5)) +
prev_plots$`16S` %>% plotly::ggplotly()
Warning: `gather_()` was deprecated in tidyr 1.2.0.
Please use `gather()` instead.
prev_plots$ITS %>% plotly::ggplotly()
Predominant phyla
ps_phyla = map(ps_phylum_filt,
function(ps_phylum_filt){
prevalenceThreshold = 0.05 * nsamples(ps_phyla)
get_counts() %>%
prevalence = rowSums(.> 0),
TotalAbundance = taxa_sums(ps_phyla)) %>%
pull(feature_id) %>%
prune_taxa(., ps_phyla) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
as('data.frame') %>%
mutate(TissueType = fct_relevel(TissueType, 'Ovule'))
return(ps_phyla)})
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
prev_corn_phyla = map(ps_phyla_ra,
function(ps_phyla_ra){
relative_counts = ps_phyla_ra %>% get_counts()
tax = ps_phyla_ra %>% get_tax()
relative_counts %>%
left_join(metadata, by='SampleID') %>%
left_join(tax, by='feature_id') %>%
group_by(Phylum) %>%
mutate(prevalence_entire_dataset = sum(counts > 0) / n(),
mean_relative_abundance_entire_dataset = mean(counts)) %>%
group_by(Phylum, Corn_Genotype) %>%
mean_relative_abundance = mean(counts),
ungroup()})
prev_corn_phyla$`16S` %>%
distinct(Phylum, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
arrange(desc(total)) %>%
kbl(format = 'html', col.names = c('Phylum', 'Total Relative Abundance',
kable_classic(full_width = F, html_font = "Times New Roman")
| Phylum |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Proteobacteria |
0.866 |
0.829 |
0.899 |
| Firmicutes |
0.065 |
0.118 |
0.016 |
| Actinobacteriota |
0.038 |
0.041 |
0.035 |
| Bacteroidota |
0.030 |
0.010 |
0.050 |
prev_corn_phyla$ITS %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
#filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kbl(format = 'html', col.names = c('Phylum', 'Total Relative Abundance',
kable_classic(full_width = F, html_font = "Times New Roman")
| Phylum |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Ascomycota |
0.999 |
0.999 |
0.999 |
| Basidiomycota |
0.001 |
0.001 |
0.001 |
Removed taxa that are present in less than 5% of samples
for ASV level dataset. This will be used for differential abundance
testing at ASV level.
ps_prevf = map2(ps_phylum_filt, prev,
function(ps_phylum_filt, prev){
prevalenceThreshold = 0.05 * nsamples(ps_phylum_filt)
keepTaxa = filter(prev, prevalence >= prevalenceThreshold) %>%
prune_taxa(keepTaxa, ps_phylum_filt) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
ps_prevf_ra = map(ps_prevf, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_prevf_clr = map(ps_prevf, microbiome::transform, 'clr')
ps_prevf_alr = map(ps_prevf, microbiome::transform, 'alr', shift=1)
ps_prevf
$`16S`
otu_table() OTU Table: [ 287 taxa and 48 samples ]
sample_data() Sample Data: [ 48 samples by 10 sample variables ]
tax_table() Taxonomy Table: [ 287 taxa by 7 taxonomic ranks ]
phy_tree() Phylogenetic Tree: [ 287 tips and 278 internal nodes ]
$ITS
phyloseq-class experiment-level objectotu_table() OTU Table: [ 51 taxa and 50 samples ]
Agglomerated counts at both genus level and species
level.
ps_genus = map(ps_phylum_filt,
ps_genus = tax_glom(ps_phylum_filt, "Genus", NArm = TRUE)
prevalenceThreshold = 0.05 * nsamples(ps_genus)
ps_genus = ps_genus %>%
get_counts() %>%
summarise(feature_id = feature_id,
prevalence = rowSums(.> 0),
TotalAbundance = taxa_sums(ps_genus)) %>%
filter(prevalence >= prevalenceThreshold) %>%
prune_taxa(., ps_genus) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
prune_taxa(taxa_sums(.) > 0, .)
sample_data(ps_genus) = sample_data(ps_genus) %>%
as('data.frame') %>%
mutate(TissueType = fct_relevel(TissueType, 'Ovule'))
return(ps_genus)})
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
ps_genus_ra = map(ps_genus, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_genus_clr = map(ps_genus, microbiome::transform, 'clr')
ps_genus_alr = map(ps_genus, microbiome::transform, 'alr', shift=1)
ps_species = map(ps_phylum_filt,
function(ps_phylum_filt){
ps_species = ps_species %>%
get_counts() %>%
summarise(feature_id = feature_id,
TotalAbundance = taxa_sums(ps_species)) %>%
filter(prevalence >= prevalenceThreshold) %>%
pull(feature_id) %>%
prune_taxa(., ps_species) %>%
prune_samples(sample_sums(.) >= 500, .) %>%
sample_data(ps_species) = sample_data(ps_species) %>%
as('data.frame') %>%
return(ps_species)})
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
Please use `reframe()` instead.
When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an ungrouped data frame and adjust accordingly.
ps_species_ra = map(ps_species, ~transform_sample_counts(.x, function(x){x / sum(x)}))
ps_species_clr = map(ps_species, microbiome::transform, 'clr')
ps_species_alr = map(ps_species, microbiome::transform, 'alr', shift=1)
Most prevalent genera
prev_corn_genus = map(ps_genus_ra,
function(ps_genus_ra){
relative_counts = ps_genus_ra %>% get_counts()
tax = ps_genus_ra %>% get_tax()
pivot_longer(-feature_id, names_to = 'SampleID', values_to = 'counts') %>%
left_join(metadata, by='SampleID') %>%
left_join(tax, by='feature_id') %>%
group_by(Genus) %>%
mutate(prevalence_entire_dataset = sum(counts > 0) / n(),
summarize(prevalence = sum(counts > 0) / n(),
mean_relative_abundance_entire_dataset = first(mean_relative_abundance_entire_dataset)) %>%
ungroup()})
prev_corn_genus$`16S` %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Genus, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kable_classic(full_width = F, html_font = "Times New Roman")
| Genus |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Pantoea |
0.461 |
0.419 |
0.500 |
| Klebsiella |
0.094 |
0.024 |
0.159 |
| Enterobacter |
0.051 |
0.101 |
0.005 |
| Carnimonas |
0.041 |
0.085 |
0.000 |
| Burkholderia-Caballeronia-Paraburkholderia |
0.030 |
0.048 |
0.013 |
| Serratia |
0.029 |
0.033 |
0.026 |
| Stenotrophomonas |
0.029 |
0.014 |
0.043 |
| Lactococcus |
0.024 |
0.048 |
0.003 |
| Sphingobacterium |
0.024 |
0.007 |
0.039 |
| Achromobacter |
0.023 |
0.008 |
0.036 |
| Rosenbergiella |
0.022 |
0.047 |
0.000 |
| Listeria |
0.019 |
0.040 |
0.000 |
| Allorhizobium-Neorhizobium-Pararhizobium-Rhizobium |
0.017 |
0.009 |
0.023 |
| Ochrobactrum |
0.017 |
0.013 |
0.020 |
| Enterococcus |
0.010 |
0.013 |
0.007 |
prev_corn_genus$ITS %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Genus, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
arrange(desc(total)) %>%
'Relative abundance B73', 'Relative abundance CML322')) %>%
| Genus |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Aspergillus |
0.552 |
0.497 |
0.608 |
| Sarocladium |
0.252 |
0.226 |
0.279 |
| Meyerozyma |
0.124 |
0.148 |
0.101 |
| Talaromyces |
0.040 |
0.076 |
0.003 |
| Trichoderma |
0.013 |
0.026 |
0.001 |
| Penicillium |
0.006 |
0.011 |
0.001 |
| Curvularia |
0.005 |
0.007 |
0.003 |
| Alternaria |
0.004 |
0.007 |
0.002 |
| Ramichloridium |
0.001 |
0.001 |
0.001 |
| Acremonium |
0.000 |
0.001 |
0.000 |
| Bipolaris |
0.000 |
0.000 |
0.000 |
| Candida |
0.000 |
0.000 |
0.000 |
| Ceriporia |
0.000 |
0.000 |
0.000 |
| Cladosporium |
0.000 |
0.000 |
0.000 |
| Cyphellophora |
0.000 |
0.000 |
0.000 |
| Exserohilum |
0.000 |
0.000 |
0.000 |
| Fomes |
0.000 |
0.000 |
0.000 |
| Fusarium |
0.000 |
0.000 |
0.000 |
| Lecanicillium |
0.000 |
0.000 |
0.000 |
| Moesziomyces |
0.000 |
0.000 |
0.000 |
| Myrothecium |
0.000 |
0.001 |
0.000 |
| Paraconiothyrium |
0.000 |
0.000 |
0.000 |
| Peniophora |
0.000 |
0.000 |
0.000 |
| Peziza |
0.000 |
0.001 |
0.000 |
| Phanerochaete |
0.000 |
0.000 |
0.000 |
| Phlebia |
0.000 |
0.000 |
0.000 |
| Pyricularia |
0.000 |
0.000 |
0.000 |
| Scopuloides |
0.000 |
0.000 |
0.000 |
| Stereum |
0.000 |
0.001 |
0.000 |
| Tinctoporellus |
0.000 |
0.000 |
0.000 |
| Trametes |
0.000 |
0.000 |
0.000 |
| unidentified |
0.000 |
0.000 |
0.000 |
Most prevalent species
prev_corn_species = map(ps_species_ra,
function(ps_species_ra){
pivot_longer(-feature_id, names_to = 'SampleID', values_to = 'counts') %>%
left_join(metadata, by='SampleID') %>%
left_join(tax, by='feature_id') %>%
group_by(Species) %>%
mutate(prevalence_entire_dataset = sum(counts > 0) / n(),
group_by(Species, Corn_Genotype) %>%
prevalence_entire_dataset = first(prevalence_entire_dataset),
mean_relative_abundance_entire_dataset = first(mean_relative_abundance_entire_dataset)) %>%
ungroup()})
prev_corn_species$`16S` %>%
rename(total = mean_relative_abundance_entire_dataset) %>%
distinct(Species, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
arrange(desc(total)) %>%
kable_classic(full_width = F, html_font = "Times New Roman")
| Species |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Pantoea_ananatis |
0.193 |
0.207 |
0.173 |
| Burkholderia_gladioli |
0.152 |
0.195 |
0.091 |
| Listeria_grayi |
0.070 |
0.119 |
0.000 |
| Lactococcus_lactis |
0.068 |
0.077 |
0.056 |
| Sphingobacterium_siyangense |
0.050 |
0.001 |
0.120 |
| Lactococcus_garvieae |
0.043 |
0.073 |
0.000 |
| Sphingobacterium_thalpophilum |
0.032 |
0.000 |
0.079 |
| Sphingomonas_phyllosphaerae |
0.030 |
0.000 |
0.073 |
| Acinetobacter_baumannii |
0.029 |
0.000 |
0.070 |
| Pseudomonas_psychrotolerans |
0.026 |
0.008 |
0.052 |
| Comamonas_sediminis |
0.023 |
0.000 |
0.056 |
| Corynebacterium_kroppenstedtii |
0.021 |
0.036 |
0.000 |
| Flavobacterium_anatoliense |
0.019 |
0.000 |
0.047 |
| Sphingobacterium_multivorum |
0.015 |
0.012 |
0.019 |
| Staphylococcus_sciuri |
0.015 |
0.026 |
0.000 |
| Devosia_riboflavina |
0.014 |
0.000 |
0.035 |
| uncultured_Tistrella |
0.013 |
0.023 |
0.000 |
| uncultured_bacterium |
0.010 |
0.014 |
0.003 |
prev_corn_species$ITS %>%
distinct(Species, Corn_Genotype, mean_relative_abundance, total) %>%
mutate(mean_relative_abundance= round(mean_relative_abundance, 3),
total = round(total, 3)) %>%
pivot_wider(names_from = Corn_Genotype, values_from = mean_relative_abundance) %>%
filter(total >= 0.01) %>%
arrange(desc(total)) %>%
kbl(format = 'html', col.names = c('Species', 'Total Relative Abundance',
'Relative abundance B73', 'Relative abundance CML322')) %>%
kable_classic(full_width = F, html_font = "Times New Roman")
| Species |
Total Relative Abundance |
Relative abundance B73 |
Relative abundance CML322 |
| Aspergillus_flavus |
0.567 |
0.525 |
0.608 |
| Sarocladium_zeae |
0.259 |
0.234 |
0.284 |
| Meyerozyma_caribbica |
0.135 |
0.168 |
0.102 |
| Aspergillus_niger |
0.011 |
0.021 |
0.000 |
| Talaromyces_purpureogenus |
0.010 |
0.019 |
0.000 |
Below are barplots of relative taxon abundances for 16S sequencing
with samples grouped according to similarity using the neatmap
method.
## https://github.com/google/palette.js/blob/79a703df344e3b24380ce1a211a2df7f2d90ca22/palette.js#L802
mpn65 = c('#ff0029','#377eb8','#66a61e','#984ea3','#00d2d5','#ff7f00','#af8d00','#7f80cd','#b3e900','#c42e60','#a65628',
'#f781bf','#8dd3c7','#bebada','#fb8072','#80b1d3','#fdb462','#fccde5','#bc80bd','#ffed6f','#c4eaff','#cf8c00',
'#f2b94f','#eff26e','#e43872','#d9b100','#9d7a00','#698cff','#d9d9d9','#00d27e','#d06800','#009f82','#c49200',
'#cbe8ff','#fecddf','#c27eb6','#8cd2ce','#c4b8d9','#f883b0','#a49100','#f48800','#27d0df','#a04a9b')
map(rank_names(ps_genus$`16S`)[2:6], function(tax_rank){
df = ps_genus$`16S` %>%
speedyseq::mutate_sample_data(condition = condition_w_rep) %>%
transform(transform = "compositional") %>%
p = plot_composition(df, x.label='condition', otu.sort = 'abundance', sample.sort='neatmap') +
guides(fill = guide_legend(ncol = 1)) +
scale_fill_manual(values=mpn65) +
theme_minimal() +
theme(axis.text.x=element_text(angle=90,hjust=0, vjust=0.5)) +
labs(x = "Sample condition",
fill = tax_rank)
})
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
NA
The next two sets are done with ITS counts
but made the same way as above.
map(rank_names(ps_genus$ITS)[2:6], function(tax_rank){
df = ps_genus$ITS %>%
speedyseq::mutate_sample_data(condition = condition_w_rep) %>%
transform(transform = "compositional") %>%
p = plot_composition(df, x.label='condition', otu.sort = 'abundance', sample.sort='neatmap') +
guides(fill = guide_legend(ncol = 1)) +
scale_fill_manual(values=mpn65) +
theme_minimal() +
labs(x = "Sample condition",
y = "Relative abundance",
title = glue("ITS Relative abundance at {tax_rank} level"),
fill = tax_rank)
p %>% plotly::ggplotly()
})
Warning: stress is (nearly) zero: you may have insufficient data
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
NA
prune_taxa(names(sort(taxa_sums(ps_genus_ra$`16S`),decreasing = TRUE)[1:30]), ps_genus_ra$`16S`) %>%
speedyseq::plot_heatmap(method = "NMDS", distance = "bray", sample.label = 'condition', taxa.label='Genus') +
theme(axis.text.x=element_text(angle=90,hjust=0, vjust=0.5, size = 10),
strip.text.x = element_text(size = 16), plot.title = element_text(size=22)) +
labs(title = '16S Heatmap of top 30 most abundant genera')

prune_taxa(names(sort(taxa_sums(ps_genus_ra$ITS),decreasing = TRUE)[1:30]), ps_genus_ra$ITS) %>%
speedyseq::plot_heatmap(method = "NMDS", distance = "bray", sample.label = 'condition', taxa.label='Genus') +
theme(axis.text.x=element_text(angle=90,hjust=0, vjust=0.5, size = 10),
strip.text.x = element_text(size = 16), plot.title = element_text(size=22)) +
labs(title = 'ITS Heatmap of top 30 most abundant genera')

save.image(here('src/16_and_ITS_import.RData'))
LS0tDQp0aXRsZTogIjE2UyBhbmQgSVRTIGRhdGEgaW1wb3J0Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICBlZGl0b3Jfb3B0aW9uczogDQogICAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHFpaW1lMlIpDQpsaWJyYXJ5KGdnZWFzeSkNCmxpYnJhcnkocGh5bG9zZXEpDQpsaWJyYXJ5KEJpb3N0cmluZ3MpDQpsaWJyYXJ5KGdnc2lnbmlmKQ0KbGlicmFyeShnZ2Vhc3kpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoQUxERXgyKQ0KbGlicmFyeShtaWNyb2Jpb21lKSANCmxpYnJhcnkodmVnYW4pDQpsaWJyYXJ5KEFOQ09NQkMpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGdnc3RhdHNwbG90KQ0KbGlicmFyeShiZWVzd2FybSkNCmxpYnJhcnkobXNhKQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShnZ3RleHQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHRpZHlIZWF0bWFwKQ0KbGlicmFyeShTcGllY0Vhc2kpDQpsaWJyYXJ5KE5ldENvTWkpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkodGlkeWdyYXBoKQ0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcucmV0aW5hID0gMSwgZHBpPTQ1MCkNCm9wdGlvbnMoZHBseXIuc3VtbWFyaXNlLmluZm9ybT1GKQ0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpDQpzZXNzaW9uSW5mbygpICU+JQ0KICBjYXB0dXJlLm91dHB1dChzZXNzaW9uSW5mbygpKSAlPiUNCiAgd3JpdGVfbGluZXMoaGVyZSgnb3V0cHV0L3Nlc3Npb25faW5mby50eHQnKSkNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQptZXRhZGF0YSA9IHJlYWRfdHN2KGhlcmUoJ2RhdGEnLCAnbWV0YWRhdGFfbWVyZ2VkLnRzdicpLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQ0KICByZW5hbWUoVGltZVBvaW50ID0gVGltZXBvaW50KSAlPiUNCiAgbXV0YXRlKFRpbWVQb2ludCA9IHN0cl9yZXBsYWNlKFRpbWVQb2ludCwgJ14nLCAnVCcpLA0KICAgICAgICAgY29uZGl0aW9uID0gc3RyX2MoQ29yblZhcmlldHksIEZ1bmdhbFN0cmFpbiwgVGlzc3VlRXh0cmFjdGlvbiwgc2VwPSdfJykpICU+JQ0KICByZW5hbWUoQ29ybl9HZW5vdHlwZSA9IENvcm5WYXJpZXR5LCBGdW5nYWxfVHJlYXRtZW50ID0gRnVuZ2FsU3RyYWluLCBUaXNzdWVfRXh0cmFjdGlvbiA9IFRpc3N1ZUV4dHJhY3Rpb24pDQpgYGANCg0KYGBge3J9DQpnZXRfY291bnRzID0gZnVuY3Rpb24oLikgb3R1X3RhYmxlKC4pICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignZmVhdHVyZV9pZCcpICU+JSBhc190aWJibGUoKQ0KZ2V0X3RheCA9IGZ1bmN0aW9uKC4pIHRheF90YWJsZSguKSAlPiUgZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oJ2ZlYXR1cmVfaWQnKSAlPiUgYXNfdGliYmxlKCkNCmdldF9xemEgPSBmdW5jdGlvbihmaWxlcGF0aCl7cmVhZF9xemEoZmlsZXBhdGgpJGRhdGEgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCdTYW1wbGVJRCcpfQ0KZ2V0X3NhbXBsZV9kYXRhID0gZnVuY3Rpb24oLikgZGF0YS5mcmFtZShzYW1wbGVfZGF0YSguKSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignU2FtcGxlSUQnKSAlPiUgYXNfdGliYmxlKCkNCmNoYW5nZV9pdHNfaWRzX3RvX21hdGNoXzE2UyA9IC4gJT4lIA0KICBtdXRhdGUoU2FtcGxlSUQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgJyguKj8pLSguKiknLCAnXFwyLVxcMScpLA0KICAgICAgICAgU2FtcGxlSUQgPSBzdHJfcmVwbGFjZV9hbGwoU2FtcGxlSUQsIGMoJy1CNzMnPSctMzczJywgJy1DMzIyJz0nLTMyMicpKSkNCmNoYW5nZV8xNlNfaWRzX3RvX21hdGNoX0lUUyA9IC4gJT4lIA0KICBtdXRhdGUoU2FtcGxlSUQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgJyguKj8pLSguKiknLCAnXFwyLVxcMScpLA0KICAgICAgICAgU2FtcGxlSUQgPSBzdHJfcmVwbGFjZV9hbGwoU2FtcGxlSUQsIGMoJy0zNzMnPSctQjczJywgJy0zMjInPSctQzMyMicpKSkNCmBgYA0KDQpgYGB7cn0NCnBzID0gbGlzdCgnMTZTJz0gcXphX3RvX3BoeWxvc2VxKGZlYXR1cmVzID0gaGVyZSgnZGF0YScsICcxNlMnLCAndGFibGVfd29fb3V0bGllcnMucXphJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWU9aGVyZSgnZGF0YScsICcxNlMnLCAncm9vdGVkLXRyZWUucXphJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRheG9ub215PWhlcmUoJ2RhdGEnLCAnMTZTJywgJ21lcmdlZC10YXhvbm9teS5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGE9aGVyZSgnZGF0YScsICcxNlMnLCAnbWV0YWRhdGFfZmlsdGVyZWQudHN2JyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRtcD0nQzovVXNlcnMvYnJpYW4ubWFjay9Eb3dubG9hZHMvdG1wJykgJT4lIA0KICAgICAgICAgICAgc3Vic2V0X3NhbXBsZXMoQ29yblZhcmlldHkgIT0gJ2R1bW15JyAmIFRpbWVwb2ludCA9PSAnMicgJiBUaXNzdWVUeXBlID09ICdPdnVsZScpICU+JQ0KICAgICAgICAgICAgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSwNCiAgICAgICAgICAnSVRTJyA9IHF6YV90b19waHlsb3NlcShmZWF0dXJlcyA9IGhlcmUoJ2RhdGEnLCAnSVRTJywgJ3RhYmxlLW5vLW1pdG9jaG9uZHJpYS1uby1jaGxvcm9wbGFzdC5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlZT1oZXJlKCdkYXRhJywgJ0lUUycsICdyb290ZWQtdHJlZS5xemEnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b25vbXk9aGVyZSgnZGF0YScsICdJVFMnLCAnbWVyZ2VkLXRheG9ub215LnF6YScpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YT1oZXJlKCdkYXRhJywgJ0lUUycsICdtZXRhZGF0YV9tZXJnZWQudHN2JyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRtcD0nQzovVXNlcnMvYnJpYW4ubWFjay9Eb3dubG9hZHMvdG1wJykgICU+JSANCiAgICAgICAgICAgIHN1YnNldF9zYW1wbGVzKENvcm5WYXJpZXR5ICAhPSAnZHVtbXknICYgVGltZXBvaW50ID09ICcyJyAmIFRpc3N1ZVR5cGUgPT0gJ092dWxlJykgJT4lDQogICAgICAgICAgICBwcnVuZV90YXhhKHRheGFfc3VtcyguKSA+IDAsIC4pDQogICAgICAgICAgICApDQpwcyA9IG1hcChwcywgZnVuY3Rpb24oLngpew0KICBzYW1wbGVfZGF0YSgueCkgPSBnZXRfc2FtcGxlX2RhdGEoLngpICU+JSANCiAgICByZW5hbWUoVGltZVBvaW50ID0gVGltZXBvaW50KSAlPiUNCiAgICBtdXRhdGUoVGltZVBvaW50ID0gc3RyX3JlcGxhY2UoVGltZVBvaW50LCAnXicsICdUJyksDQogICAgICAgICAgIGNvbmRpdGlvbiA9IHN0cl9jKENvcm5WYXJpZXR5LCBGdW5nYWxTdHJhaW4sIFRpc3N1ZUV4dHJhY3Rpb24sIHNlcD0nXycpKSAlPiUNCiAgICByZW5hbWUoQ29ybl9HZW5vdHlwZSA9IENvcm5WYXJpZXR5LCBGdW5nYWxfVHJlYXRtZW50ID0gRnVuZ2FsU3RyYWluLCBUaXNzdWVfRXh0cmFjdGlvbiA9IFRpc3N1ZUV4dHJhY3Rpb24pICU+JQ0KICAgIGdyb3VwX2J5KGNvbmRpdGlvbikgJT4lDQogICAgbXV0YXRlKGJpb2xvZ2ljYWxfcmVwID0gcm93X251bWJlcigpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGNvbmRpdGlvbl93X3JlcCA9IHN0cl9jKGNvbmRpdGlvbiwgYmlvbG9naWNhbF9yZXAsIHNlcD0nXycpKSAlPiUNCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoJ1NhbXBsZUlEJykNCiAgcmV0dXJuKC54KX0pDQpzYW1wbGVfZGF0YShwcyRJVFMpID0gZ2V0X3NhbXBsZV9kYXRhKHBzJElUUykgJT4lDQogIGNoYW5nZV9pdHNfaWRzX3RvX21hdGNoXzE2UygpICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoJ1NhbXBsZUlEJykNCnJlYWRzX2RmID0gcmVhZHMgJT4lIG1hcCh+dGliYmxlKHNlcXVlbmNlID0gYXMuY2hhcmFjdGVyKC54KSwgZmVhdHVyZV9pZCA9IG5hbWVzKC54KSkpDQpwcyAlPiUgaW1hcCh+Z2V0X2NvdW50cygueCkgJT4lIA0KICAgICAgICAgICAgICBsZWZ0X2pvaW4ocmVhZHNfZGZbWy55XV0sIGJ5PSdmZWF0dXJlX2lkJykgJT4lDQogICAgICAgICAgICAgIGxlZnRfam9pbihnZXRfdGF4KC54KSwgYnk9J2ZlYXR1cmVfaWQnKSAlPiUNCiAgICAgICAgICAgICAgcmVuYW1lKGFsbF9vZihnZXRfc2FtcGxlX2RhdGEoLngpICU+JSBwdWxsKFNhbXBsZUlELCBuYW1lPWNvbmRpdGlvbl93X3JlcCkpKSAlPiUNCiAgICAgICAgICAgICAgd3JpdGVfY3N2KGhlcmUoc3RyX2dsdWUoJ291dHB1dC9yYXdfY291bnRzX2FuZF90YXhvbm9teV97Lnl9LmNzdicpKSkNCiAgICAgICAgICAgICAgKQ0KYGBgDQoNClJlbW92ZWQgdGF4YSBub3QgYXNzaWduZWQgdG8gYSBwaHlsdW0uIEFmdGVyIHJlbW92aW5nIHRoZXNlIHRheGEsIDEgMTZTIHNhbXBsZXMgd2FzIHJlbW92ZWQgZHVlIHRvIGhhdmluZyB0b3RhbCBjb3VudHMgPD0gMTUwMC4NCg0KYGBge3J9DQpwc19waHlsdW1fZmlsdCA9IGxpc3QoJzE2UycgPSBzdWJzZXRfdGF4YShwcyRgMTZTYCwgIWlzLm5hKFBoeWx1bSkgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIVBoeWx1bSAlaW4lIGMoJycsICd1bmNoYXJhY3Rlcml6ZWQnLCAndW5pZGVudGlmaWVkJykgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIUtpbmdkb20gJWluJSBjKCdkX19FdWthcnlvdGEnKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSAxNTAwLCAuKSAlPiUgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSwNCiAgICAgICAgICAgICAgICAgICAgICAnSVRTJyA9IHN1YnNldF90YXhhKHBzJElUUywgIWlzLm5hKFBoeWx1bSkgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhUGh5bHVtICVpbiUgYygiIiwgJ3VuY2hhcmFjdGVyaXplZCcsICd1bmlkZW50aWZpZWQnKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSAxNTAwLCAuKSAlPiUgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKSkNCnBzX3BoeWx1bV9maWx0X3RheCA9IHBzX3BoeWx1bV9maWx0ICU+JSBtYXAoZ2V0X3RheCkNCnBzX3BoeWx1bV9maWx0X2NvdW50cyA9IHBzX3BoeWx1bV9maWx0ICU+JSBtYXAoZ2V0X2NvdW50cykNCnBzX3BoeWx1bV9maWx0X3JhID0gbWFwKHBzX3BoeWx1bV9maWx0LCB+dHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoLngsIGZ1bmN0aW9uKHgpe3ggLyBzdW0oeCl9KSkNCnBzX3BoeWx1bV9maWx0DQpgYGANCmBgYHtyfQ0KcmVhZHMgPSBsaXN0KCcxNlMnID0gcmVhZF9xemEoaGVyZSgnZGF0YScsICcxNlMnLCAncmVwLXNlcXMtZmlsdGVyZWQucXphJykpJGRhdGEsDQogICAgICAgICAgICAgJ0lUUycgPSByZWFkX3F6YShoZXJlKCdkYXRhJywgJ0lUUycsICdyZXAtc2Vxcy1maWx0ZXJlZC5xemEnKSkkZGF0YSkNCmBgYA0KDQpgYGB7cn0NCnBzX3BoeWx1bV9maWx0ICU+JSBpbWFwKGZ1bmN0aW9uKHBzX3BoeWx1bV9maWx0LCBzZXFfdHlwZSl7DQogIHBzX3BoeWx1bV9maWx0ICU+JSBnZXRfdGF4KCkgJT4lDQogICAgc3VtbWFyaXplKGFjcm9zcygtZmVhdHVyZV9pZCwgfnN1bSghaXMubmEoLngpKS8gbigpKSkgJT4lDQogICAgcm91bmQoMikgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAnVGF4b25vbWljIHJhbmsnLCB2YWx1ZXNfdG8gPSBzZXFfdHlwZSkNCiAgfSkgJT4lDQogIHJlZHVjZShmdWxsX2pvaW4sIGJ5PSdUYXhvbm9taWMgcmFuaycpICU+JQ0KICBrYmwoZm9ybWF0ID0gJ2h0bWwnLCBjYXB0aW9uPSdGcmFjdGlvbiBvZiBBU1ZzIGNsYXNzaWZpZWQgYXQgZWFjaCByYW5rJykgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEYsIGh0bWxfZm9udCA9ICJUaW1lcyBOZXcgUm9tYW4iKSANCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD02fQ0KcHNfcGh5bHVtX2ZpbHQgJT4lIGltYXAoZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHQsIHNlcV90eXBlKXsNCiAgcHNfcGh5bHVtX2ZpbHQgJT4lIHNhbXBsZV9zdW1zKCkgJT4lIGVuZnJhbWUobmFtZSA9ICdTYW1wbGVJRCcsIHZhbHVlID0gJ3RvdGFsX2NvdW50cycpICU+JQ0KICAgIGxlZnRfam9pbihtZXRhZGF0YSwgYnk9J1NhbXBsZUlEJykgJT4lDQogICAgbXV0YXRlKHNlcV90eXBlID0gc2VxX3R5cGUpDQogIH0pICU+JQ0KICBiaW5kX3Jvd3MoKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWNvbmRpdGlvbiwgeT10b3RhbF9jb3VudHMpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsNCiAgZ2diZWVzd2FybTo6Z2VvbV9xdWFzaXJhbmRvbShhbHBoYSA9IDAuMywgd2lkdGg9MC4yLCBncm91cE9uWD1UUlVFKSArDQogIGZhY2V0X3dyYXAofnNlcV90eXBlLCBuY29sPTEsIHNjYWxlcyA9ICdmcmVlJykgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBsYWJzKHRpdGxlPSdUb3RhbCBudW1iZXIgb2YgY291bnRzIGZvciBlYWNoIHNhbXBsZScpICsNCiAgZ2dlYXN5OjplYXN5X3JvdGF0ZV94X2xhYmVscygpICsNCiAgZ2dlYXN5OjplYXN5X2NlbnRlcl90aXRsZSgpDQpgYGANCg0KPGJyPjxicj48YnI+DQoNClJhcmVmeWluZyB0byAxNTY5IGNvdW50cy4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpwc19waHlsdW1fZmlsdF9yYXJlZmllZCA9ICBwc19waHlsdW1fZmlsdCAlPiUgbWFwKA0KICB+cmFyZWZ5X2V2ZW5fZGVwdGgoLngsIHNhbXBsZS5zaXplID0gMTU2OSwgcm5nc2VlZD0xMTAwICwgcmVwbGFjZT1GQUxTRSkgJT4lDQogICAgIHBydW5lX3RheGEodGF4YV9zdW1zKC4pID4gMCwgLikpDQpwc19waHlsdW1fZmlsdF9yYXJlZmllZA0KYGBgDQoNCjxicj48YnI+PGJyPjxicj48YnI+PGJyPg0KDQpgYGB7ciwgZmlnLndpZHRoPTEyfQ0KcHJldiA9IG1hcChwc19waHlsdW1fZmlsdF9yYSwNCiAgICAgICAgICAgIGZ1bmN0aW9uKHBzX3BoeWx1bV9maWx0X3JhKXsNCiAgICAgICAgICAgICAgcmVsYXRpdmVfY291bnRzID0gcHNfcGh5bHVtX2ZpbHRfcmEgJT4lIGdldF9jb3VudHMoKQ0KICAgICAgICAgICAgICB0YXggPSBwc19waHlsdW1fZmlsdF9yYSAlPiUgZ2V0X3RheCgpDQogICAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyAlPiUgDQogICAgICAgICAgICAgICAgcmVmcmFtZShmZWF0dXJlX2lkID0gZmVhdHVyZV9pZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlID0gcm93U3VtcyguPiAwKSAtMSwgI3N1YnRyYWN0ZWQgMSBiZWNhdXNlIGZlYXR1cmVfaWQgY29sdW1uIGlzIGFsd2F5cyBjb3VudGVkDQogICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxfcmVsYXRpdmVfYWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzX3BoeWx1bV9maWx0X3JhKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgUGh5bHVtID0gdGF4JFBoeWx1bSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBHZW51cyA9IHRheCRHZW51cywNCiAgICAgICAgICAgICAgICAgICAgICAgICBTcGVjaWVzID0gdGF4JFNwZWNpZXMpfSkNCnByZXZfcGxvdHMgPSBwcmV2ICU+JSBpbWFwKGZ1bmN0aW9uKHByZXYsIHNlcV90eXBlKXsNCiAgcHJldiAlPiUgDQogICAgbXV0YXRlKHByZXZhbGVuY2UgPSBwcmV2YWxlbmNlIC8gbnNhbXBsZXMocHNfcGh5bHVtX2ZpbHRbW3NlcV90eXBlXV0pKSAlPiUNCiAgZ2dwbG90KGFlcyh0b3RhbF9yZWxhdGl2ZV9hYnVuZGFuY2UsIHByZXZhbGVuY2UsIGNvbG9yPVBoeWx1bSwgdGV4dD1nbHVlKCdHZW51czoge0dlbnVzfTxicj5TcGVjaWVzOiB7U3BlY2llc30nKSkpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjA1LCBhbHBoYSA9IDAuNSwgbGluZXR5cGUgPSAyKSArIA0KICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhID0gMC42KSArDQogICAgc2NhbGVfeF9sb2cxMCgpICsgeGxhYigiVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlIikgKyB5bGFiKCJQcmV2YWxlbmNlIFtGcmFjdGlvbiBTYW1wbGVzXSIpICsNCiAgICBmYWNldF93cmFwKH5QaHlsdW0pICsNCiAgICBsYWJzKHRpdGxlID0gZ2x1ZSgne3NlcV90eXBlfSBBU1YgUHJldmFsZW5jZScpKSArDQogICAgdGhlbWVfZ3JheSgpICsNCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpfSkNCmBgYA0KDQpgYGB7ciwgb3V0LndpZHRoPTEyfQ0KcHJldl9wbG90cyRgMTZTYCAlPiUgcGxvdGx5OjpnZ3Bsb3RseSgpDQpgYGANCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCnByZXZfcGxvdHMkSVRTICU+JSBwbG90bHk6OmdncGxvdGx5KCkNCmBgYA0KUHJlZG9taW5hbnQgcGh5bGENCmBgYHtyfQ0KcHNfcGh5bGEgPSBtYXAocHNfcGh5bHVtX2ZpbHQsIA0KICAgICAgICAgICAgICAgZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHQpew0KICAgICAgICAgICAgICAgICBwc19waHlsYSA9IHRheF9nbG9tKHBzX3BoeWx1bV9maWx0LCAiUGh5bHVtIiwgTkFybSA9IFRSVUUpDQogICAgICAgICAgICAgICAgIHByZXZhbGVuY2VUaHJlc2hvbGQgPSAgMC4wNSAqIG5zYW1wbGVzKHBzX3BoeWxhKQ0KICAgICAgICAgICAgICAgICBwc19waHlsYSA9IHBzX3BoeWxhICU+JSANCiAgICAgICAgICAgICAgICAgICBnZXRfY291bnRzKCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShmZWF0dXJlX2lkID0gZmVhdHVyZV9pZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmFsZW5jZSA9IHJvd1N1bXMoLj4gMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsQWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzX3BoeWxhKSkgJT4lDQogICAgICAgICAgICAgICAgICAgZmlsdGVyKHByZXZhbGVuY2UgPj0gcHJldmFsZW5jZVRocmVzaG9sZCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHB1bGwoZmVhdHVyZV9pZCkgJT4lDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfdGF4YSguLCBwc19waHlsYSkgJT4lDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSA1MDAsIC4pICU+JSANCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKHRheGFfc3VtcyguKSA+IDAsIC4pDQogICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKHBzX3BoeWxhKSA9IHNhbXBsZV9kYXRhKHBzX3BoeWxhKSAlPiUgDQogICAgICAgICAgICAgICAgICAgYXMoJ2RhdGEuZnJhbWUnKSAlPiUNCiAgICAgICAgICAgICAgICAgICBtdXRhdGUoVGlzc3VlVHlwZSA9IGZjdF9yZWxldmVsKFRpc3N1ZVR5cGUsICdPdnVsZScpKQ0KICAgICAgICAgICAgICAgICByZXR1cm4ocHNfcGh5bGEpfSkNCnBzX3BoeWxhX3JhID0gbWFwKHBzX3BoeWxhLCB+dHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoLngsIGZ1bmN0aW9uKHgpe3ggLyBzdW0oeCl9KSkNCnByZXZfY29ybl9waHlsYSA9IG1hcChwc19waHlsYV9yYSwNCiAgICAgICAgICAgIGZ1bmN0aW9uKHBzX3BoeWxhX3JhKXsNCiAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyA9IHBzX3BoeWxhX3JhICU+JSBnZXRfY291bnRzKCkNCiAgICAgICAgICAgICAgdGF4ID0gcHNfcGh5bGFfcmEgJT4lIGdldF90YXgoKQ0KICAgICAgICAgICAgICByZWxhdGl2ZV9jb3VudHMgJT4lIA0KICAgICAgICAgICAgICAgIHBpdm90X2xvbmdlcigtZmVhdHVyZV9pZCwgbmFtZXNfdG8gPSAnU2FtcGxlSUQnLCB2YWx1ZXNfdG8gPSAnY291bnRzJykgJT4lDQogICAgICAgICAgICAgICAgbGVmdF9qb2luKG1ldGFkYXRhLCBieT0nU2FtcGxlSUQnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odGF4LCBieT0nZmVhdHVyZV9pZCcpICU+JQ0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KFBoeWx1bSkgJT4lDQogICAgICAgICAgICAgICAgbXV0YXRlKHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQgPSBzdW0oY291bnRzID4gMCkgLyBuKCksDQogICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0ID0gbWVhbihjb3VudHMpKSAlPiUNCiAgICAgICAgICAgICAgICBncm91cF9ieShQaHlsdW0sIENvcm5fR2Vub3R5cGUpICU+JQ0KICAgICAgICAgICAgICAgIHN1bW1hcml6ZShwcmV2YWxlbmNlID0gc3VtKGNvdW50cyA+IDApIC8gbigpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSA9IG1lYW4oY291bnRzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCA9IGZpcnN0KHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCA9IGZpcnN0KG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSkgJT4lDQogICAgICAgICAgICAgICAgdW5ncm91cCgpfSkNCnByZXZfY29ybl9waHlsYSRgMTZTYCAlPiUgDQogIHJlbmFtZSh0b3RhbCA9IG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0KSAlPiUNCiAgZGlzdGluY3QoUGh5bHVtLCBDb3JuX0dlbm90eXBlLCBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgdG90YWwpICU+JQ0KICBtdXRhdGUobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2U9IHJvdW5kKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCAzKSwNCiAgICAgICAgIHRvdGFsID0gcm91bmQodG90YWwsIDMpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvcm5fR2Vub3R5cGUsIHZhbHVlc19mcm9tID0gbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UpICU+JQ0KICBmaWx0ZXIodG90YWwgPj0gMC4wMSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICU+JQ0KICBrYmwoZm9ybWF0ID0gJ2h0bWwnLCBjb2wubmFtZXMgPSBjKCdQaHlsdW0nLCAnVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JlbGF0aXZlIGFidW5kYW5jZSBCNzMnLCAnUmVsYXRpdmUgYWJ1bmRhbmNlIENNTDMyMicpKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpDQpwcmV2X2Nvcm5fcGh5bGEkSVRTICU+JSANCiAgcmVuYW1lKHRvdGFsID0gbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQpICU+JQ0KICBkaXN0aW5jdChQaHlsdW0sIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogICNmaWx0ZXIodG90YWwgPj0gMC4wMSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICU+JQ0KICBrYmwoZm9ybWF0ID0gJ2h0bWwnLCBjb2wubmFtZXMgPSBjKCdQaHlsdW0nLCAnVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JlbGF0aXZlIGFidW5kYW5jZSBCNzMnLCAnUmVsYXRpdmUgYWJ1bmRhbmNlIENNTDMyMicpKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpDQpgYGANCjxicj48YnI+PGJyPiBSZW1vdmVkIHRheGEgdGhhdCBhcmUgcHJlc2VudCBpbiBsZXNzIHRoYW4gNSUgb2Ygc2FtcGxlcyBmb3IgQVNWIGxldmVsIGRhdGFzZXQuIFRoaXMgd2lsbCBiZSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIHRlc3RpbmcgYXQgQVNWIGxldmVsLg0KYGBge3J9DQpwc19wcmV2ZiA9IG1hcDIocHNfcGh5bHVtX2ZpbHQsIHByZXYsDQogICAgICAgICAgICAgICBmdW5jdGlvbihwc19waHlsdW1fZmlsdCwgcHJldil7DQogICAgICAgICAgICAgICAgIHByZXZhbGVuY2VUaHJlc2hvbGQgPSAgMC4wNSAqIG5zYW1wbGVzKHBzX3BoeWx1bV9maWx0KQ0KICAgICAgICAgICAgICAgICBrZWVwVGF4YSA9IGZpbHRlcihwcmV2LCBwcmV2YWxlbmNlID49IHByZXZhbGVuY2VUaHJlc2hvbGQpICU+JSANCiAgICAgICAgICAgICAgICAgICBwdWxsKGZlYXR1cmVfaWQpDQogICAgICAgICAgICAgICAgIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX3BoeWx1bV9maWx0KSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV9zYW1wbGVzKHNhbXBsZV9zdW1zKC4pID49IDUwMCwgLikgJT4lIA0KICAgICAgICAgICAgICAgICAgIHBydW5lX3RheGEodGF4YV9zdW1zKC4pID4gMCwgLil9KQ0KcHNfcHJldmZfcmEgPSBtYXAocHNfcHJldmYsIH50cmFuc2Zvcm1fc2FtcGxlX2NvdW50cygueCwgZnVuY3Rpb24oeCl7eCAvIHN1bSh4KX0pKQ0KcHNfcHJldmZfY2xyID0gbWFwKHBzX3ByZXZmLCBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0sICdjbHInKQ0KcHNfcHJldmZfYWxyID0gbWFwKHBzX3ByZXZmLCBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0sICdhbHInLCBzaGlmdD0xKQ0KcHNfcHJldmYNCmBgYA0KDQo8YnI+PGJyPjxicj4gQWdnbG9tZXJhdGVkIGNvdW50cyBhdCBib3RoIGdlbnVzIGxldmVsIGFuZCBzcGVjaWVzIGxldmVsLg0KDQpgYGB7cn0NCnBzX2dlbnVzID0gbWFwKHBzX3BoeWx1bV9maWx0LCANCiAgICAgICAgICAgICAgIGZ1bmN0aW9uKHBzX3BoeWx1bV9maWx0KXsNCiAgICAgICAgICAgICAgICAgcHNfZ2VudXMgPSB0YXhfZ2xvbShwc19waHlsdW1fZmlsdCwgIkdlbnVzIiwgTkFybSA9IFRSVUUpDQogICAgICAgICAgICAgICAgIHByZXZhbGVuY2VUaHJlc2hvbGQgPSAgMC4wNSAqIG5zYW1wbGVzKHBzX2dlbnVzKQ0KICAgICAgICAgICAgICAgICBwc19nZW51cyA9IHBzX2dlbnVzICU+JSANCiAgICAgICAgICAgICAgICAgICBnZXRfY291bnRzKCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShmZWF0dXJlX2lkID0gZmVhdHVyZV9pZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmFsZW5jZSA9IHJvd1N1bXMoLj4gMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsQWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzX2dlbnVzKSkgJT4lDQogICAgICAgICAgICAgICAgICAgZmlsdGVyKHByZXZhbGVuY2UgPj0gcHJldmFsZW5jZVRocmVzaG9sZCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHB1bGwoZmVhdHVyZV9pZCkgJT4lDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfdGF4YSguLCBwc19nZW51cykgJT4lDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyguKSA+PSA1MDAsIC4pICU+JSANCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKHRheGFfc3VtcyguKSA+IDAsIC4pDQogICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKHBzX2dlbnVzKSA9IHNhbXBsZV9kYXRhKHBzX2dlbnVzKSAlPiUgDQogICAgICAgICAgICAgICAgICAgYXMoJ2RhdGEuZnJhbWUnKSAlPiUNCiAgICAgICAgICAgICAgICAgICBtdXRhdGUoVGlzc3VlVHlwZSA9IGZjdF9yZWxldmVsKFRpc3N1ZVR5cGUsICdPdnVsZScpKQ0KICAgICAgICAgICAgICAgICByZXR1cm4ocHNfZ2VudXMpfSkNCnBzX2dlbnVzX3JhID0gbWFwKHBzX2dlbnVzLCB+dHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoLngsIGZ1bmN0aW9uKHgpe3ggLyBzdW0oeCl9KSkNCnBzX2dlbnVzX2NsciA9IG1hcChwc19nZW51cywgbWljcm9iaW9tZTo6dHJhbnNmb3JtLCAnY2xyJykNCnBzX2dlbnVzX2FsciA9IG1hcChwc19nZW51cywgbWljcm9iaW9tZTo6dHJhbnNmb3JtLCAnYWxyJywgc2hpZnQ9MSkNCnBzX3NwZWNpZXMgPSBtYXAocHNfcGh5bHVtX2ZpbHQsIA0KICAgICAgICAgICAgICAgZnVuY3Rpb24ocHNfcGh5bHVtX2ZpbHQpew0KICAgICAgICAgICAgICAgICBwc19zcGVjaWVzID0gdGF4X2dsb20ocHNfcGh5bHVtX2ZpbHQsICJTcGVjaWVzIiwgTkFybSA9IFRSVUUpDQogICAgICAgICAgICAgICAgIHByZXZhbGVuY2VUaHJlc2hvbGQgPSAgMC4wNSAqIG5zYW1wbGVzKHBzX3NwZWNpZXMpDQogICAgICAgICAgICAgICAgIHBzX3NwZWNpZXMgPSBwc19zcGVjaWVzICU+JSANCiAgICAgICAgICAgICAgICAgICBnZXRfY291bnRzKCkgJT4lIA0KICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShmZWF0dXJlX2lkID0gZmVhdHVyZV9pZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmFsZW5jZSA9IHJvd1N1bXMoLj4gMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsQWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzX3NwZWNpZXMpKSAlPiUNCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIocHJldmFsZW5jZSA+PSBwcmV2YWxlbmNlVGhyZXNob2xkKSAlPiUgDQogICAgICAgICAgICAgICAgICAgcHVsbChmZWF0dXJlX2lkKSAlPiUNCiAgICAgICAgICAgICAgICAgICBwcnVuZV90YXhhKC4sIHBzX3NwZWNpZXMpICU+JQ0KICAgICAgICAgICAgICAgICAgIHBydW5lX3NhbXBsZXMoc2FtcGxlX3N1bXMoLikgPj0gNTAwLCAuKSAlPiUgDQogICAgICAgICAgICAgICAgICAgcHJ1bmVfdGF4YSh0YXhhX3N1bXMoLikgPiAwLCAuKQ0KICAgICAgICAgICAgICAgICBzYW1wbGVfZGF0YShwc19zcGVjaWVzKSA9IHNhbXBsZV9kYXRhKHBzX3NwZWNpZXMpICU+JSANCiAgICAgICAgICAgICAgICAgICBhcygnZGF0YS5mcmFtZScpICU+JQ0KICAgICAgICAgICAgICAgICAgIG11dGF0ZShUaXNzdWVUeXBlID0gZmN0X3JlbGV2ZWwoVGlzc3VlVHlwZSwgJ092dWxlJykpDQogICAgICAgICAgICAgICAgIHJldHVybihwc19zcGVjaWVzKX0pDQpwc19zcGVjaWVzX3JhID0gbWFwKHBzX3NwZWNpZXMsIH50cmFuc2Zvcm1fc2FtcGxlX2NvdW50cygueCwgZnVuY3Rpb24oeCl7eCAvIHN1bSh4KX0pKQ0KcHNfc3BlY2llc19jbHIgPSBtYXAocHNfc3BlY2llcywgbWljcm9iaW9tZTo6dHJhbnNmb3JtLCAnY2xyJykNCnBzX3NwZWNpZXNfYWxyID0gbWFwKHBzX3NwZWNpZXMsIG1pY3JvYmlvbWU6OnRyYW5zZm9ybSwgJ2FscicsIHNoaWZ0PTEpDQpgYGANCg0KTW9zdCBwcmV2YWxlbnQgZ2VuZXJhDQpgYGB7cn0NCnByZXZfY29ybl9nZW51cyA9IG1hcChwc19nZW51c19yYSwNCiAgICAgICAgICAgIGZ1bmN0aW9uKHBzX2dlbnVzX3JhKXsNCiAgICAgICAgICAgIHJlbGF0aXZlX2NvdW50cyA9IHBzX2dlbnVzX3JhICU+JSBnZXRfY291bnRzKCkNCiAgICAgICAgICAgICAgdGF4ID0gcHNfZ2VudXNfcmEgJT4lIGdldF90YXgoKQ0KICAgICAgICAgICAgICByZWxhdGl2ZV9jb3VudHMgJT4lIA0KICAgICAgICAgICAgICAgIHBpdm90X2xvbmdlcigtZmVhdHVyZV9pZCwgbmFtZXNfdG8gPSAnU2FtcGxlSUQnLCB2YWx1ZXNfdG8gPSAnY291bnRzJykgJT4lDQogICAgICAgICAgICAgICAgbGVmdF9qb2luKG1ldGFkYXRhLCBieT0nU2FtcGxlSUQnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odGF4LCBieT0nZmVhdHVyZV9pZCcpICU+JQ0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KEdlbnVzKSAlPiUNCiAgICAgICAgICAgICAgICBtdXRhdGUocHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCA9IHN1bShjb3VudHMgPiAwKSAvIG4oKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQgPSBtZWFuKGNvdW50cykpICU+JQ0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KEdlbnVzLCBDb3JuX0dlbm90eXBlKSAlPiUNCiAgICAgICAgICAgICAgICBzdW1tYXJpemUocHJldmFsZW5jZSA9IHN1bShjb3VudHMgPiAwKSAvIG4oKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UgPSBtZWFuKGNvdW50cyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZhbGVuY2VfZW50aXJlX2RhdGFzZXQgPSBmaXJzdChwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQgPSBmaXJzdChtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkpICU+JQ0KICAgICAgICAgICAgICAgIHVuZ3JvdXAoKX0pDQpwcmV2X2Nvcm5fZ2VudXMkYDE2U2AgJT4lIA0KICByZW5hbWUodG90YWwgPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkgJT4lDQogIGRpc3RpbmN0KEdlbnVzLCBDb3JuX0dlbm90eXBlLCBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgdG90YWwpICU+JQ0KICBtdXRhdGUobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2U9IHJvdW5kKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCAzKSwNCiAgICAgICAgIHRvdGFsID0gcm91bmQodG90YWwsIDMpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvcm5fR2Vub3R5cGUsIHZhbHVlc19mcm9tID0gbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UpICU+JQ0KICBmaWx0ZXIodG90YWwgPj0gMC4wMSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICU+JQ0KICBrYmwoZm9ybWF0ID0gJ2h0bWwnLCBjb2wubmFtZXMgPSBjKCdHZW51cycsICdUb3RhbCBSZWxhdGl2ZSBBYnVuZGFuY2UnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVsYXRpdmUgYWJ1bmRhbmNlIEI3MycsICdSZWxhdGl2ZSBhYnVuZGFuY2UgQ01MMzIyJykpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiVGltZXMgTmV3IFJvbWFuIikNCnByZXZfY29ybl9nZW51cyRJVFMgJT4lIA0KICByZW5hbWUodG90YWwgPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkgJT4lDQogIGRpc3RpbmN0KEdlbnVzLCBDb3JuX0dlbm90eXBlLCBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSwgdG90YWwpICU+JQ0KICBtdXRhdGUobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2U9IHJvdW5kKG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCAzKSwNCiAgICAgICAgIHRvdGFsID0gcm91bmQodG90YWwsIDMpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvcm5fR2Vub3R5cGUsIHZhbHVlc19mcm9tID0gbWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UpICU+JQ0KICAjZmlsdGVyKHRvdGFsID49IDAuMDEpICU+JQ0KICBhcnJhbmdlKGRlc2ModG90YWwpKSAlPiUNCiAga2JsKGZvcm1hdCA9ICdodG1sJywgY29sLm5hbWVzID0gYygnR2VudXMnLCAnVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JlbGF0aXZlIGFidW5kYW5jZSBCNzMnLCAnUmVsYXRpdmUgYWJ1bmRhbmNlIENNTDMyMicpKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpDQpgYGANCk1vc3QgcHJldmFsZW50IHNwZWNpZXMNCmBgYHtyfQ0KcHJldl9jb3JuX3NwZWNpZXMgPSBtYXAocHNfc3BlY2llc19yYSwNCiAgICAgICAgICAgIGZ1bmN0aW9uKHBzX3NwZWNpZXNfcmEpew0KICAgICAgICAgICAgcmVsYXRpdmVfY291bnRzID0gcHNfc3BlY2llc19yYSAlPiUgZ2V0X2NvdW50cygpDQogICAgICAgICAgICAgIHRheCA9IHBzX3NwZWNpZXNfcmEgJT4lIGdldF90YXgoKQ0KICAgICAgICAgICAgICByZWxhdGl2ZV9jb3VudHMgJT4lIA0KICAgICAgICAgICAgICAgIHBpdm90X2xvbmdlcigtZmVhdHVyZV9pZCwgbmFtZXNfdG8gPSAnU2FtcGxlSUQnLCB2YWx1ZXNfdG8gPSAnY291bnRzJykgJT4lDQogICAgICAgICAgICAgICAgbGVmdF9qb2luKG1ldGFkYXRhLCBieT0nU2FtcGxlSUQnKSAlPiUNCiAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odGF4LCBieT0nZmVhdHVyZV9pZCcpICU+JQ0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KFNwZWNpZXMpICU+JQ0KICAgICAgICAgICAgICAgIG11dGF0ZShwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0ID0gc3VtKGNvdW50cyA+IDApIC8gbigpLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCA9IG1lYW4oY291bnRzKSkgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3BlY2llcywgQ29ybl9HZW5vdHlwZSkgJT4lDQogICAgICAgICAgICAgICAgc3VtbWFyaXplKHByZXZhbGVuY2UgPSBzdW0oY291bnRzID4gMCkgLyBuKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlID0gbWVhbihjb3VudHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlX2VudGlyZV9kYXRhc2V0ID0gZmlyc3QocHJldmFsZW5jZV9lbnRpcmVfZGF0YXNldCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlX2VudGlyZV9kYXRhc2V0ID0gZmlyc3QobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2VfZW50aXJlX2RhdGFzZXQpKSAlPiUNCiAgICAgICAgICAgICAgICB1bmdyb3VwKCl9KQ0KcHJldl9jb3JuX3NwZWNpZXMkYDE2U2AgJT4lIA0KICByZW5hbWUodG90YWwgPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkgJT4lDQogIGRpc3RpbmN0KFNwZWNpZXMsIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogIGZpbHRlcih0b3RhbCA+PSAwLjAxKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNvbC5uYW1lcyA9IGMoJ1NwZWNpZXMnLCAnVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JlbGF0aXZlIGFidW5kYW5jZSBCNzMnLCAnUmVsYXRpdmUgYWJ1bmRhbmNlIENNTDMyMicpKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpDQpwcmV2X2Nvcm5fc3BlY2llcyRJVFMgJT4lIA0KICByZW5hbWUodG90YWwgPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZV9lbnRpcmVfZGF0YXNldCkgJT4lDQogIGRpc3RpbmN0KFNwZWNpZXMsIENvcm5fR2Vub3R5cGUsIG1lYW5fcmVsYXRpdmVfYWJ1bmRhbmNlLCB0b3RhbCkgJT4lDQogIG11dGF0ZShtZWFuX3JlbGF0aXZlX2FidW5kYW5jZT0gcm91bmQobWVhbl9yZWxhdGl2ZV9hYnVuZGFuY2UsIDMpLA0KICAgICAgICAgdG90YWwgPSByb3VuZCh0b3RhbCwgMykpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29ybl9HZW5vdHlwZSwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbGF0aXZlX2FidW5kYW5jZSkgJT4lDQogIGZpbHRlcih0b3RhbCA+PSAwLjAxKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGtibChmb3JtYXQgPSAnaHRtbCcsIGNvbC5uYW1lcyA9IGMoJ1NwZWNpZXMnLCAnVG90YWwgUmVsYXRpdmUgQWJ1bmRhbmNlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JlbGF0aXZlIGFidW5kYW5jZSBCNzMnLCAnUmVsYXRpdmUgYWJ1bmRhbmNlIENNTDMyMicpKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIE5ldyBSb21hbiIpDQpgYGANCg0KDQpCZWxvdyBhcmUgYmFycGxvdHMgb2YgcmVsYXRpdmUgdGF4b24gYWJ1bmRhbmNlcyBmb3IgMTZTIHNlcXVlbmNpbmcgd2l0aCBzYW1wbGVzIGdyb3VwZWQgYWNjb3JkaW5nIHRvIHNpbWlsYXJpdHkgdXNpbmcgdGhlIG5lYXRtYXAgbWV0aG9kLiANCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMCwgd2FybmluZz1GQUxTRSwgZmlnLnNob3c9J2hpZGUnfQ0KIyMgaHR0cHM6Ly9naXRodWIuY29tL2dvb2dsZS9wYWxldHRlLmpzL2Jsb2IvNzlhNzAzZGYzNDRlM2IyNDM4MGNlMWEyMTFhMmRmN2YyZDkwY2EyMi9wYWxldHRlLmpzI0w4MDINCm1wbjY1ID0gYygnI2ZmMDAyOScsJyMzNzdlYjgnLCcjNjZhNjFlJywnIzk4NGVhMycsJyMwMGQyZDUnLCcjZmY3ZjAwJywnI2FmOGQwMCcsJyM3ZjgwY2QnLCcjYjNlOTAwJywnI2M0MmU2MCcsJyNhNjU2MjgnLA0KICAgICAgICAgJyNmNzgxYmYnLCcjOGRkM2M3JywnI2JlYmFkYScsJyNmYjgwNzInLCcjODBiMWQzJywnI2ZkYjQ2MicsJyNmY2NkZTUnLCcjYmM4MGJkJywnI2ZmZWQ2ZicsJyNjNGVhZmYnLCcjY2Y4YzAwJywNCiAgICAgICAgICcjMWI5ZTc3JywnI2Q5NWYwMicsJyNlNzI5OGEnLCcjZTZhYjAyJywnI2E2NzYxZCcsJyMwMDk3ZmYnLCcjMDBkMDY3JywnIzAwMDAwMCcsJyMyNTI1MjUnLCcjNTI1MjUyJywnIzczNzM3MycsDQogICAgICAgICAnIzk2OTY5NicsJyNiZGJkYmQnLCcjZjQzNjAwJywnIzRiYTkzYicsJyM1Nzc5YmInLCcjOTI3YWNjJywnIzk3ZWUzZicsJyNiZjM5NDcnLCcjOWY1YjAwJywnI2Y0ODc1OCcsJyM4Y2FlZDYnLA0KICAgICAgICAgJyNmMmI5NGYnLCcjZWZmMjZlJywnI2U0Mzg3MicsJyNkOWIxMDAnLCcjOWQ3YTAwJywnIzY5OGNmZicsJyNkOWQ5ZDknLCcjMDBkMjdlJywnI2QwNjgwMCcsJyMwMDlmODInLCcjYzQ5MjAwJywNCiAgICAgICAgICcjY2JlOGZmJywnI2ZlY2RkZicsJyNjMjdlYjYnLCcjOGNkMmNlJywnI2M0YjhkOScsJyNmODgzYjAnLCcjYTQ5MTAwJywnI2Y0ODgwMCcsJyMyN2QwZGYnLCcjYTA0YTliJykNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9DQptYXAocmFua19uYW1lcyhwc19nZW51cyRgMTZTYClbMjo2XSwgZnVuY3Rpb24odGF4X3Jhbmspew0KICBkZiA9IHBzX2dlbnVzJGAxNlNgICU+JSANCiAgICBzcGVlZHlzZXE6Om11dGF0ZV9zYW1wbGVfZGF0YShjb25kaXRpb24gPSBjb25kaXRpb25fd19yZXApICU+JQ0KICAgIHRyYW5zZm9ybSh0cmFuc2Zvcm0gPSAiY29tcG9zaXRpb25hbCIpICU+JQ0KICAgIGFnZ3JlZ2F0ZV9yYXJlKGxldmVsID0gdGF4X3JhbmssIGRldGVjdGlvbiA9IDAuMDUsIHByZXZhbGVuY2UgPSAwLjA1KSANCiAgcCA9IHBsb3RfY29tcG9zaXRpb24oZGYsIHgubGFiZWw9J2NvbmRpdGlvbicsIG90dS5zb3J0ID0gJ2FidW5kYW5jZScsIHNhbXBsZS5zb3J0PSduZWF0bWFwJykgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW1wbjY1KSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsaGp1c3Q9MCwgdmp1c3Q9MC41KSkgKw0KICBsYWJzKHggPSAiU2FtcGxlIGNvbmRpdGlvbiIsDQogICAgICAgeSA9ICJSZWxhdGl2ZSBhYnVuZGFuY2UiLA0KICAgICAgIHRpdGxlID0gZ2x1ZSgiMTZTIFJlbGF0aXZlIGFidW5kYW5jZSBhdCB7dGF4X3Jhbmt9IGxldmVsIiksIA0KICAgICAgIGZpbGwgPSB0YXhfcmFuaykNCiAgICAgcCAlPiUgcGxvdGx5OjpnZ3Bsb3RseSgpDQp9KQ0KYGBgDQoNCg0KPGJyPjxicj48YnI+PGJyPjxicj48YnI+IFRoZSBuZXh0IHR3byBzZXRzIGFyZSBkb25lIHdpdGggSVRTIGNvdW50cyBidXQgbWFkZSB0aGUgc2FtZSB3YXkgYXMgYWJvdmUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0NCm1hcChyYW5rX25hbWVzKHBzX2dlbnVzJElUUylbMjo2XSwgZnVuY3Rpb24odGF4X3Jhbmspew0KICBkZiA9IHBzX2dlbnVzJElUUyAlPiUgDQogICAgc3BlZWR5c2VxOjptdXRhdGVfc2FtcGxlX2RhdGEoY29uZGl0aW9uID0gY29uZGl0aW9uX3dfcmVwKSAlPiUNCiAgICB0cmFuc2Zvcm0odHJhbnNmb3JtID0gImNvbXBvc2l0aW9uYWwiKSAlPiUNCiAgICBhZ2dyZWdhdGVfcmFyZShsZXZlbCA9IHRheF9yYW5rLCBkZXRlY3Rpb24gPSAwLjA1LCBwcmV2YWxlbmNlID0gMC4wNSkgDQogIHAgPSBwbG90X2NvbXBvc2l0aW9uKGRmLCB4LmxhYmVsPSdjb25kaXRpb24nLCBvdHUuc29ydCA9ICdhYnVuZGFuY2UnLCBzYW1wbGUuc29ydD0nbmVhdG1hcCcpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1tcG42NSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLGhqdXN0PTAsIHZqdXN0PTAuNSkpICsNCiAgbGFicyh4ID0gIlNhbXBsZSBjb25kaXRpb24iLA0KICAgICAgIHkgPSAiUmVsYXRpdmUgYWJ1bmRhbmNlIiwNCiAgICAgICB0aXRsZSA9IGdsdWUoIklUUyBSZWxhdGl2ZSBhYnVuZGFuY2UgYXQge3RheF9yYW5rfSBsZXZlbCIpLCANCiAgICAgICBmaWxsID0gdGF4X3JhbmspDQogIHAgJT4lIHBsb3RseTo6Z2dwbG90bHkoKQ0KfSkNCmBgYA0KDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OCwgd2FybmluZz1GQUxTRX0NCnBydW5lX3RheGEobmFtZXMoc29ydCh0YXhhX3N1bXMocHNfZ2VudXNfcmEkYDE2U2ApLGRlY3JlYXNpbmcgPSBUUlVFKVsxOjMwXSksIHBzX2dlbnVzX3JhJGAxNlNgKSAlPiUNCiAgc3BlZWR5c2VxOjpwbG90X2hlYXRtYXAobWV0aG9kID0gIk5NRFMiLCBkaXN0YW5jZSA9ICJicmF5Iiwgc2FtcGxlLmxhYmVsID0gJ2NvbmRpdGlvbicsIHRheGEubGFiZWw9J0dlbnVzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsaGp1c3Q9MCwgdmp1c3Q9MC41LCBzaXplID0gMTApLA0KICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkgKw0KICBsYWJzKHRpdGxlID0gJzE2UyBIZWF0bWFwIG9mIHRvcCAzMCBtb3N0IGFidW5kYW50IGdlbmVyYScpDQpgYGANCg0KPGJyPjxicj48YnI+PGJyPjxicj48YnI+DQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OCwgd2FybmluZz1GQUxTRX0NCnBydW5lX3RheGEobmFtZXMoc29ydCh0YXhhX3N1bXMocHNfZ2VudXNfcmEkSVRTKSxkZWNyZWFzaW5nID0gVFJVRSlbMTozMF0pLCBwc19nZW51c19yYSRJVFMpICU+JQ0KICBzcGVlZHlzZXE6OnBsb3RfaGVhdG1hcChtZXRob2QgPSAiTk1EUyIsIGRpc3RhbmNlID0gImJyYXkiLCBzYW1wbGUubGFiZWwgPSAnY29uZGl0aW9uJywgdGF4YS5sYWJlbD0nR2VudXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCxoanVzdD0wLCB2anVzdD0wLjUsIHNpemUgPSAxMCksDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKSArDQogIGxhYnModGl0bGUgPSAnSVRTIEhlYXRtYXAgb2YgdG9wIDMwIG1vc3QgYWJ1bmRhbnQgZ2VuZXJhJykNCmBgYA0KYGBge3J9DQpzYXZlLmltYWdlKGhlcmUoJ3NyYy8xNl9hbmRfSVRTX2ltcG9ydC5SRGF0YScpKQ0KYGBgDQoNCg==